package cn.qn.service.impl;

import cn.qn.common.constants.AppConstants;
import cn.qn.common.util.JwtUtil;
import cn.qn.dto.user.*;
import cn.qn.enums.*;
import cn.qn.exceptions.BizException;
import cn.qn.moudle.sys.Area;
import cn.qn.moudle.sys.City;
import cn.qn.moudle.sys.Province;
import cn.qn.moudle.sys.SysMessage;
import cn.qn.moudle.user.AppUser;
import cn.qn.service.IAppUserService;
import cn.qn.service.article.ArticleService;
import cn.qn.service.sys.AreaService;
import cn.qn.service.sys.CityService;
import cn.qn.service.sys.ProvinceService;
import cn.qn.service.sys.SysMessageService;
import cn.qn.service.user.AppUserScoreService;
import cn.qn.service.user.AppUserService;
import cn.qn.utils.SerialNoUtils;
import cn.qn.utils.SmsUtils;
import cn.qn.vo.AppUserResetPassVo;
import cn.qn.vo.AppUserVo;
import com.alibaba.dubbo.config.annotation.Reference;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author: Bryant
 * @date: 2019/7/15
 * @description:
 */
@Service
@Data
public class AppUserServiceImpl implements IAppUserService {

    private static Logger logger = LoggerFactory.getLogger(AppUserServiceImpl.class);
    @Resource
    RedisTemplate<String, Object> redisTemplate;

    @Reference(timeout = 30000,check = false)
    private AppUserService appUserService;

    @Reference(timeout = 30000,check = false)
    private ArticleService articleService;

    @Reference(timeout = 30000,check = false)
    private SysMessageService sysMessageService;

    @Reference(timeout = 30000,check = false)
    private AppUserScoreService appUserScoreService;

    @Reference(timeout = 30000,check = false)
    private ProvinceService provinceService;

    @Reference(timeout = 30000,check = false)
    private CityService cityService;

    @Reference(timeout = 30000,check = false)
    private AreaService areaService;
    @Autowired
    private SmsUtils smsUtils;

    @Value("${aliyun.oss.showUrl}")
    private String showUrl;

    @Override
    public String pwdLogin(LoginPwdDTO dto) throws BizException{
        AppUser appUser = appUserService.findByMobile(dto.getMobile());
        String password = dto.getPassword();
        if (appUser == null) {
            throw new BizException("账号不存在");
        }
        if(StringUtils.isEmpty(appUser.getLoginPass())){
            throw new BizException("用户未设置登录密码，请用短信验证码快捷登录");
        }
        if (!new BCryptPasswordEncoder().matches(password, appUser.getLoginPass())) {
            throw new BizException("账号或密码错误");
        }
        if (AppUserStatusEnum.LOCKED.equals(appUser.getStatus())) {
            throw new BizException("账号已被锁定");
        }
        String authToken = JwtUtil.generateAppToken(appUser.getUserId());
        redisTemplate.opsForValue().set(AppConstants.APP_TOKEN_PREFIX + authToken, appUser, AppConstants.TOKEN_EXPIRE_DAY, TimeUnit.DAYS);
        return authToken;
    }

    @Override
    public String smsCodeLogin(MobileCheckDTO dto) throws BizException{
        boolean flag = checkMobileCode(dto,SmsTemplatesEnum.LOGIN_CONFIRM);
        if(!flag){
            throw new BizException("短信验证码错误");
        }
        AppUser appUser = appUserService.findByMobile(dto.getMobile());
        if (appUser == null) {
            throw new BizException("账号不存在");
        }
        if (AppUserStatusEnum.LOCKED.equals(appUser.getStatus())) {
            throw new BizException("账号已被锁定");
        }
        String authToken = JwtUtil.generateAppToken(appUser.getUserId());
        redisTemplate.opsForValue().set(AppConstants.APP_TOKEN_PREFIX + authToken, appUser, AppConstants.TOKEN_EXPIRE_DAY, TimeUnit.DAYS);
        return authToken;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String register(RegisterDTO dto) throws BizException{
        boolean flag = checkMobileCode(new MobileCheckDTO(dto.getMobile(),dto.getSmsCode()),SmsTemplatesEnum.USER_REGISTER);
        if(!flag){
            throw new BizException("短信验证码错误");
        }
        dto.setHeadImg(AppConstants.USER_DEFUALT_HEAD);
        AppUser appUser = appUserService.createAppUser(dto);
        String authToken = JwtUtil.generateAppToken(appUser.getUserId());
        redisTemplate.opsForValue().set(AppConstants.APP_TOKEN_PREFIX + authToken, appUser, AppConstants.TOKEN_EXPIRE_DAY, TimeUnit.DAYS);
        SysMessage message =new SysMessage();
        message.setUserId(appUser.getUserId());
        message.setContent("恭喜你注册成功");
        message.setCreateBy("Sys");
        message.setHasRead(BooleanEnum.FALSE.getStringCode());
        message.setTitle(SysMessageTypeEnum.SYS_NOTICE.getDesc());
        message.setType(SysMessageTypeEnum.SYS_NOTICE.getCode());
        sysMessageService.insert(message);
        return authToken;
    }

    @Override
    public List<AppUserAttentionDTO> queryMyAttentions(String userId) {
        return appUserService.queryMyAttentions(userId);
    }

    @Override
    public List<AppUserAttentionDTO> queryMyThreeAttentions(String userId) {
        return appUserService.queryMyThreeAttentions(userId);
    }

    @Override
    public void attentionOneUser(String userId, String attentionId) throws BizException{
        appUserService.attentionOneUser(userId,attentionId);
    }

    @Override
    public void cancleAttentionOneUser(String userId, String attentionId) throws BizException{
        appUserService.cancleAttentionOneUser(userId,attentionId);
    }

    @Override
    public void resetAppPassword(AppUserResetPassVo vo) {
        //TODO 是否需要再次验证验证码
        appUserService.resetAppPassword(vo.getMobile(),vo.getPassword());
    }

    @Override
    public void mobileCheck(String mobile) throws BizException {
        AppUser appUser = appUserService.findByMobile(mobile);
        if(appUser!=null){
            throw new BizException("手机号码已注册");
        }
    }

    @Override
    public void editSignature(String userId, String signature) {
        AppUser appUser = appUserService.findByUserId(userId);
        appUser.setSignature(signature);
        appUser.setUpdateTime(new Date());
        appUser.setUpdateBy(appUser.getNickName());
        appUserService.editAppUser(appUser);
    }

    @Override
    public void editNickName(String userId, String nickName) {
        AppUser appUser = appUserService.findByUserId(userId);
        appUser.setNickName(nickName);
        appUser.setUpdateTime(new Date());
        appUser.setUpdateBy(appUser.getNickName());
        appUserService.editAppUser(appUser);
    }

    @Override
    public void editBirthDay(String userId, String birthday) {
        AppUser appUser = appUserService.findByUserId(userId);
        appUser.setBirthday(birthday);
        appUser.setUpdateTime(new Date());
        appUser.setUpdateBy(appUser.getNickName());
        appUserService.editAppUser(appUser);
    }

    @Override
    public void editAddress(AppUserEditDTO vo) {
        AppUser appUser = appUserService.findByUserId(vo.getUserId());
        appUser.setProvinceCode(vo.getProvinceCode());
        appUser.setCityCode(vo.getCityCode());
        appUser.setAreaCode(vo.getAreaCode());
        appUser.setUpdateTime(new Date());
        appUser.setUpdateBy(appUser.getNickName());
        appUserService.editAppUser(appUser);
    }

    @Override
    public void editHeadImage(String userId, String headImg) {
        AppUser appUser = appUserService.findByUserId(userId);
        appUser.setHeadImg(headImg);
        appUser.setUpdateTime(new Date());
        appUser.setUpdateBy(appUser.getNickName());
        appUserService.editAppUser(appUser);
    }

    @Override
    public void editSex(String userId, String sex) {
        AppUser appUser = appUserService.findByUserId(userId);
        appUser.setSex(sex);
        appUser.setUpdateTime(new Date());
        appUser.setUpdateBy(appUser.getNickName());
        appUserService.editAppUser(appUser);
    }

    @Override
    public void sendSmsVerifyCode(String mobile, SmsTemplatesEnum templates) {
        Map<String,Object> params=new HashMap<>();
        //6位随机短信验证码
        String smsCode=SerialNoUtils.randomStrNo(6);
        //存入Redis
        redisTemplate.opsForValue().set(templates.getPrefix()+mobile,smsCode,AppConstants.SMS_CODE_EXPIRE, TimeUnit.MINUTES);
        params.put("code",smsCode);
        logger.info("短信验证码："+smsCode);
        smsUtils.recursiveSendSms(mobile, templates,params,mobile);
    }

    @Override
    public boolean checkMobileCode(MobileCheckDTO dto, SmsTemplatesEnum templates) {
        Object code = redisTemplate.opsForValue().get(templates.getPrefix()+dto.getMobile());
        if(null==code || !dto.getSmsCode().equals(code.toString())){
            return false;
        }
        return true;
    }

    @Override
    public AppUserVo findByUserId(String userId) {
        AppUserVo vo=new AppUserVo();
        AppUser appUser = appUserService.findByUserId(userId);
        BeanUtils.copyProperties(appUser,vo);
        if(StringUtils.isNotEmpty(vo.getHeadImg())){
            vo.setHeadImg(showUrl+vo.getHeadImg());
        }
        vo.setShowSex(AppUserSexEnum.getDesc(vo.getSex()));
        vo.setArticleNum(articleService.countByUserId(userId));
        vo.setAttentionNum(appUserService.countByUserId(userId));
        vo.setFans(appUserService.countByAttentionId(userId));
        vo.setCheckIn(appUserScoreService.checkedIn(userId));
        String showAddress="";
        if(StringUtils.isNotEmpty(vo.getProvinceCode())){
            Province province = provinceService.findProvinceByCode(vo.getProvinceCode());
            if(null!=province){
                showAddress=province.getName();
            }
        }
        if(StringUtils.isNotEmpty(vo.getCityCode())){
            City city = cityService.findCityByCode(vo.getCityCode());
            if(null!=city){
                showAddress+=city.getName();
            }
        }
        if(StringUtils.isNotEmpty(vo.getAreaCode())){
            Area area = areaService.getArea(vo.getAreaCode());
            if(null!=area){
                showAddress+=area.getName();
            }
        }
        vo.setShowAddress(showAddress);
        return vo;
    }

}
